﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Sys.Excel
{
    [Serializable]
    public sealed class Settings
    {
        private int _maxlevel = 0;
        private IList<ColNode> _leafNodes;
        internal static readonly IDictionary<string, CellFormatter> _dictFormatters = new Dictionary<string, CellFormatter>();

        public Settings()
        {
            this.ColumnsAutoWidth = true;
        }

        public Settings(Settings settings) : this()
        {
            if (settings == null)
            {
                return;
            }

            this.CellValueGetter = settings.CellValueGetter;
            this.ColumnsAutoWidth = settings.ColumnsAutoWidth;
            this.ColumnStyleGetter = settings.ColumnStyleGetter;
            this.DataSource = settings.DataSource;
            this.FreezeHeader = settings.FreezeHeader;
            this.HeaderCellStyle = settings.HeaderCellStyle;
            this.Name = settings.Name;
            this.PageSize = settings.PageSize;
            this.RootNodes = settings.RootNodes;
            this.RowHeight = settings.RowHeight;
            this.RowStyleGetter = settings.RowStyleGetter;
            this.SheetBuilded = settings.SheetBuilded;
            this.StartColIndex = settings.StartColIndex;
            this.StartRowIndex = settings.StartRowIndex;
            this.TotalValueGetter = settings.TotalValueGetter;
        }

        internal Settings(Settings parent_Settings, int? pageIndex) : this()
        {
            this.ParentSettings = parent_Settings;
            this.PageIndex = pageIndex;
        }

        public static void RegCellFormatter(string name, CellFormatter formatter)
        {
            lock (Settings._dictFormatters)
            {
                Settings._dictFormatters[name] = formatter;
            }
        }

        private IList<ColNode> GetLeafNodes(IList<ColNode> p_nodes)
        {
            var leafNodes = new List<ColNode>();

            if (p_nodes == null || p_nodes.Count == 0)
            {
                return leafNodes;
            }

            for (int i = 0; i < p_nodes.Count; i++)
            {
                if (p_nodes[i] == null)
                {
                    p_nodes[i] = new ColNode() { };
                }

                var p_node = p_nodes[i];

                if (p_node.ChildColNodes == null || p_node.ChildColNodes.Count == 0)
                {
                    leafNodes.Add(p_node); continue;
                }

                foreach (ColNode node in p_node.ChildColNodes)
                {
                    node.Level_Index = p_node.Level_Index + 1;
                }

                foreach (ColNode node in this.GetLeafNodes(p_node.ChildColNodes))
                {
                    leafNodes.Add(node);
                }
            }

            return leafNodes;
        }

        internal int MaxLevel
        {
            get
            {
                return this._maxlevel == 0 ? this.LeafNodes.Max(node => node.Level_Index) : this._maxlevel;
            }
        }

        public int? PageIndex { get; private set; }

        public Settings ParentSettings { get; private set; }

        public IList<ColNode> LeafNodes
        {
            get
            {
                if (this._leafNodes == null)
                {
                    this._leafNodes = this.GetLeafNodes(this.RootNodes);
                }

                return this._leafNodes;
            }
        }

        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 每页数量(当 PageSize == 0 时不分页，默认为 0)
        /// </summary>
        public uint PageSize { get; set; }

        /// <summary>
        /// Excel 起始行
        /// </summary>
        public uint StartRowIndex { get; set; }

        /// <summary>
        /// Excel 起始列
        /// </summary>
        public uint StartColIndex { get; set; }

        /// <summary>
        /// 是否冻结表头
        /// </summary>
        public bool FreezeHeader { get; set; }

        /// <summary>
        /// 行高
        /// </summary>
        public uint RowHeight { get; set; }

        /// <summary>
        /// 列宽自动
        /// </summary>
        public bool ColumnsAutoWidth { get; set; }

        /// <summary>
        /// 表头单元格样式
        /// </summary>
        public CellStyle? HeaderCellStyle { get; set; }

        /// <summary>
        /// 数据源
        /// </summary>
        public object DataSource { get; set; }

        /// <summary>
        /// 定义如何获取单元格数据
        /// </summary>
        public Func<CellValueContext, object> CellValueGetter { get; set; }

        /// <summary>
        /// 定义如何获取汇总行某列的数据
        /// </summary>
        public Func<TotalValueContext, object> TotalValueGetter { get; set; }

        /// <summary>
        /// 获取行样式
        /// </summary>
        public Func<int, object, CellStyle?> RowStyleGetter { get; set; }

        /// <summary>
        /// 获取列样式
        /// </summary>
        public Func<int, ColNode, CellStyle?> ColumnStyleGetter { get; set; }

        /// <summary>
        /// Excel 表创建后执行
        /// </summary>
        public Action<SheetBuildedContext> SheetBuilded { get; set; }

        /// <summary>
        /// 根级
        /// </summary>
        public IList<ColNode> RootNodes { get; set; }

        public sealed class CellValueContext
        {
            internal CellValueContext()
            {

            }

            internal CellValueContext Reset(
                object row, int rowIndex, ColNode column, int columnIndex,
                Func<CellValueContext, object> getter,
                bool isMerge = false)
            {
                this.Row = row;
                this.RowIndex = rowIndex;
                this.Column = column;
                this.ColumnIndex = columnIndex;
                this.DefaultGetter = getter;
                this.IsMergeFieldColumn = isMerge;

                return this;
            }

            public object Row { get; private set; }

            public int RowIndex { get; private set; }

            public ColNode Column { get; private set; }

            public int ColumnIndex { get; private set; }

            public bool IsMergeFieldColumn { get; private set; }

            public Func<CellValueContext, object> DefaultGetter { get; private set; }
        }

        public sealed class TotalValueContext
        {
            internal TotalValueContext(Settings settings, int nodeIndex, ColNode node, IList<object> nodeValues)
            {
                this.Settings = settings;
                this.NodeIndex = nodeIndex;
                this.Node = node;
                this.NodeValues = nodeValues;
            }

            public Settings Settings { get; }

            public int NodeIndex { get; }

            public ColNode Node { get; }

            public IList<object> NodeValues { get; }
        }

        public sealed class SheetBuildedContext
        {
            private Action<int, int, object> _action_setCellValue;
            private Action<int, int, CellStyle> _action_setCellStyle;
            private Action<int, int, int, int> _action_mergeCells;

            internal SheetBuildedContext(
                Settings settings,
                int headerRowCount,
                int bodyRowCount,
                int colCount,
                bool hasTotalRow,
                Action<int, int, object> action_setCellValue,
                Action<int, int, CellStyle> action_setCellStyle,
                Action<int, int, int, int> action_mergeCells)
            {
                this.Settings = settings;
                this._action_setCellValue = action_setCellValue;
                this._action_setCellStyle = action_setCellStyle;
                this._action_mergeCells = action_mergeCells;
                this.HeaderRowCount = headerRowCount;
                this.BodyRowCount = bodyRowCount;
                this.ColCount = colCount;
                this.HasTotalRow = hasTotalRow;
            }

            public Settings Settings { get; }

            public int HeaderRowCount { get; }

            public int BodyRowCount { get; }

            public int ColCount { get; }

            public bool HasTotalRow { get; }

            public void SetCellValue(int rowIndex, int colIndex, object value)
            {
                if (this._action_setCellValue != null)
                {
                    this._action_setCellValue(rowIndex, colIndex, value);
                }
            }

            public void SetCellStyle(int rowIndex, int colIndex, CellStyle style)
            {
                if (this._action_setCellStyle != null)
                {
                    this._action_setCellStyle(rowIndex, colIndex, style);
                }
            }

            public void MergeCells(int startRow, int endRow, int startCol, int endCol)
            {
                if (this._action_mergeCells != null)
                {
                    this._action_mergeCells.Invoke(startRow, endRow, startCol, endCol);
                }
            }
        }
    }
}